﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace My.InfoPathDecoder
{
    /// <summary>
    /// Class used to decode an InfoPath attachment.
    /// Pulls the file name and the decoded file from either a base 64 byte array or string.
    /// </summary>
    public class InfoPathAttachmentDecoder
    {
        //  Private string to hold the attachment name.
        string _fileName;
 
        //  Private byte array to hold the decoded attachment.
        byte[] _decodedFile;
 
        /// <summary>
        /// The name of the file within the InfoPath attachment.
        /// </summary>
        public string Filename
        {
            get { return _fileName; }
        }
 
        /// <summary>
        /// The decoded file within the InfoPath attachment.
        /// </summary>
        public byte[] DecodedFile
        {
            get { return _decodedFile; }
        }
 
        /// <summary>
        /// Constructor for the InfoPathAttachmentDecoder Class
        /// </summary>
        /// <param name="base64EncodedString">The attachment represented by a string</param>
        public InfoPathAttachmentDecoder(string base64EncodedString)
        {
            //  Use unicode encoding.
            Encoding _encoding = Encoding.Unicode;
 
            //  The byte array containing the data.
            byte[] _data = Convert.FromBase64String(base64EncodedString);
 
            //  Use a memory stream to access the data.
            using (MemoryStream _memoryStream = new MemoryStream(_data))
            {
                //  Create a binary reader from the stream.
                BinaryReader _theReader = new BinaryReader(_memoryStream);
 
                //  Create a byte array to hold the header data.
                byte[] _headerData = _theReader.ReadBytes(16);
 
                //  Find the file size before finding the file name.
                int _fileSize = (int)_theReader.ReadUInt32();
 
                //  Get the file name.
                int _attachmentNameLength = (int)_theReader.ReadUInt32() * 2;
                byte[] _fileNameBytes = _theReader.ReadBytes(_attachmentNameLength);
                _fileName = _encoding.GetString(_fileNameBytes, 0, _attachmentNameLength - 2);
 
                //  Get the decoded attachment.            
                _decodedFile = _theReader.ReadBytes(_fileSize);
            }
        }
        
        /// <summary>
        /// Constructor for the InfoPathAttachmentDecoder Class
        /// </summary>
        /// <param name="base64EncodedBytes">The attachment represented by a byte array</param>
        public InfoPathAttachmentDecoder(byte[] base64EncodedBytes) : this(Convert.ToBase64String(base64EncodedBytes)) { }
 
        /// <summary>
        /// Static method that gets the file from the attachment.
        /// </summary>
        /// <param name="base64EncodedString">The attachment represented by a string</param>
        /// <returns>Returns a byte array of the file in the attachment.</returns>
        public static byte[] DecodeInfoPathAttachment(string base64EncodedString)
        {
            //  Create an instance of the InfoPathAttachmentDecoder
            InfoPathAttachmentDecoder _infoPathAttachmentDecoder = new InfoPathAttachmentDecoder(base64EncodedString);
            
            //  Return the decoded file.
            return _infoPathAttachmentDecoder.DecodedFile;
        }
 
        /// <summary>
        /// Static method that gets the file from the attachment.
        /// </summary>
        /// <param name="base64EncodedBytes">The attachment represented by a byte array</param>
        /// <returns>Returns a byte array of the file in the attachment.</returns>
        public static byte[] DecodeInfoPathAttachment(byte[] base64EncodedBytes)
        {
            //  Create an instance of the InfoPathAttachmentDecoder
            InfoPathAttachmentDecoder _infoPathAttachmentDecoder = new InfoPathAttachmentDecoder(base64EncodedBytes);
            
            //  Return the decoded file.
            return _infoPathAttachmentDecoder.DecodedFile;
        }
    }
}
